<?php
/**
 * App Module UnInstaller (应用模块卸载器)
 * 
 * 用于测试下载开发中的应用模块到项目中
 * 
 * @since 1.0 <2015-8-29> SoChishun <14507247@qq.com> Added.
 */
// 程序版本号
$ver = '1.0';

// 定义应用目录
define('APP_PATH', '../Application/');

$am = I('am');
$mm = I('mm');
$action = I('action');
if ($am && 'uninstall' == $action) {
    $amist = new AMUnInstall();
    echo $amist->uninstall($mm, $am, APP_PATH, $msg) ? ('应用模块[' . $am . ']已成功从主模块[' . $mm . ']卸载! <a href="?r">返回</a>') : $msg;
    exit;
}
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>App Module UnInstaller - <?php echo $ver ?></title>
        <style type="text/css">body{font-size:12px;}td span{color:#F00;}td div{color:#CCC;}</style>
    </head>
    <body>
        <form method="get">
            <table>
                <tr><th>主模块名称：</th><td><?php fn_get_mmpicker(APP_PATH, $mm); ?></td></tr>
                <?php if ($mm): ?>
                    <tr><th>选择应用模块:</th><td><?php fn_get_ampicker(APP_PATH, $mm, $am); ?></td></tr>
                    <tr><th>&nbsp;</th><td><button type="submit">立即卸载</button></td></tr>
                <?php else: ?>
                    <tr><th>&nbsp;</th><td><button type="submit">选择</button></td></tr>                
                <?php endif; ?>
            </table>
            <?php if ($mm): ?>
                <input type="hidden" name="action" value="uninstall" />
            <?php endif; ?>
        </form>
    </body>
</html>
<?php

/**
 * 获取用户传递的参数
 * @param string $name
 * @param string $defv
 * @return string
 * @since 1.0 <2015-8-26> SoChishun Added.
 */
function I($name, $defv = '') {
    if (isset($_POST[$name])) {
        return $_POST[$name];
    }
    if (isset($_GET[$name])) {
        return $_GET[$name];
    }
    return $defv;
}

/**
 * 生成应用模块下拉选项
 * @param string $source App路径
 * @param string $val 默认选中值
 * @since 1.0 <2015-8-27> SoChishun Added.
 */
function fn_get_ampicker($source, $mm, $val) {
    $source = $source . $mm . '/Addon/';
    if (!is_dir($source)) {
        echo '[APP_PATH]不是有效目录!';
        exit;
    }
    echo '<select name="am">';
    if ($handle = opendir($source)) {
        while (false !== ( $f = readdir($handle) )) {
            if ('.' == $f || '..' == $f) {
                continue;
            }
            if (is_file($source . '/' . $f . '/Install/module.config.php')) {
                echo '<option value="', $f, '"', ($val == $f ? 'selected="selected"' : ''), '>', $f, '</option>';
            }
        }
        closedir($handle);
    }
    echo '</select>';
}

/**
 * 生成主模块下拉选项
 * @param string $source App路径
 * @param string $val 默认选中值
 * @since 1.0 <2015-8-28> SoChishun Added.
 */
function fn_get_mmpicker($source, $val) {
    if (!is_dir($source)) {
        echo '[APP_PATH]不是有效目录!';
        exit;
    }
    echo '<select name="mm">';
    $a_static = array('Common', 'Runtime');
    if ($handle = opendir($source)) {
        while (false !== ( $f = readdir($handle) )) {
            if ('.' == $f || '..' == $f) {
                continue;
            }
            if (is_dir($source . '/' . $f) && !in_array($f, $a_static)) {
                echo '<option value="', $f, '"', ($val == $f ? 'selected="selected"' : ''), '>', $f, '</option>';
            }
        }
        closedir($handle);
    }
    echo '</select>';
}

/**
 * 应用模块安装类
 * @since 1.0 <2015-8-28> SoChishun Added.
 */
class AMUnInstall {

    /**
     * 模块安装源目录
     * @var string
     */
    protected $am_dist_path;

    /**
     * 主模块目录
     * @var string 
     */
    protected $mm_path;

    /**
     * VendorCode
     * @var string
     */
    protected $vendor_code;

    /**
     * 安装应用模块
     * @param string $mm 主模块名称
     * @param string $am 应用模块名称
     * @param string $app_path APP_PATH
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     * @example
     *  echo $amist->install($ver, $am, APP_PATH, $msg) ? ('应用模块[' . $am . ']已成功安装到主模块[' . $mm . ']! <a href="?r">返回</a>') : $msg;
     */
    function uninstall($mm, $am, $app_path, &$msg) {
        if (!$am) {
            $msg = '模块[' . $am . ']不是有效模块!';
            return false;
        }
        $this->am_dist_path = APP_PATH . $mm . '/Addon/' . $am . '/';
        $this->mm_path = $app_path . $mm . '/';
        $uninstalled_path = APP_PATH . $mm . '/Addon/_' . $am . '/';

        if (!is_dir($this->am_dist_path)) {
            $msg = '模块[' . $am . ']还没有安装!';
            return false;
        }

        // 删除数据库对象
        if (!$this->uninst_sql($mm, $am, $msg)) {
            return false;
        }
        // 删除权限菜单
        if (!$this->uninst_permission($mm, $am, $msg)) {
            if ($msg) { // 没有权限数据不算操作失败
                return false;
            }
        }
        // 删除Controller、Model、View、Common中的相关文件
        //$this->uninst_delfile($mm, $am, $msg);
        // 卸载完成,源目录重名为"{目录}",不删除,卸载或重装的时候会用到.
        //rename($this->am_dist_path, $uninstalled_path);
        return true;
    }

    /**
     * 删除数据库对象
     * @param string $mm 主模块名称
     * @param string $am 应用模块名称
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     */
    protected function uninst_sql($mm, $am, &$msg = '') {
        // 打开数据库连接
        if (false === ($link = $this->db_connect($mm, $msg))) {
            return false;
        }
        // 执行安装语句
        $cnf_path = $this->am_dist_path . 'Install/module.config.php'; // 用户目录,存放用户级的文件
        if (!file_exists($cnf_path)) {
            $msg = '应用模块[' . $am . ']配置文件不存在!';
            return false;
        }
        $errs = false;
        $am_config = require $cnf_path;
        $asqlsections = $am_config['UNINSTALL']['1_SQL'];
        $asqltpl = array(
            '1_Table' => 'drop table if exists {0}',
            '2_View' => 'drop view if exists {0}',
            '3_Procedure' => 'drop procedure {0}',
            '4_Function' => 'drop function {0}',
            '5_Sqls' => '{0}',
        );
        foreach ($asqlsections as $key => $section) {
            if (!$section) {
                continue;
            }
            if (!array_key_exists($key, $asqltpl)) {
                continue;
            }
            $sql = $asqltpl[$key];
            foreach ($section as $item) {
                if (!$item) {
                    continue;
                }
                mysql_query(str_replace('{0}', $item, $sql));
            }
        }
        // 关闭数据库连接
        mysql_close($link);
        return true;
    }

    /**
     * 删除权限菜单
     * @param string $mm 主模块名称
     * @param string $am 应用模块名称
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     */
    protected function uninst_permission($mm, $am, &$msg = '') {
        $cnf_path = $this->am_dist_path . 'Install/module.config.php'; // 用户目录,存放用户级的文件
        if (!file_exists($cnf_path)) {
            $msg = '应用模块[' . $am . ']配置文件不存在!';
            return false;
        }
        if (false === ($link = $this->db_connect($mm, $msg))) {
            return false;
        }
        $sql = "delete from think_auth_rule where addon='$am'";
        mysql_query($sql);
        mysql_close($link);
        return true;
    }

    /**
     * 删除逻辑脚本文件
     * @param string $mm 主模块名称
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     */
    protected function uninst_delfile($mm, &$msg = '') {
        $dirs = array('Controller', 'Model', 'Common');
        $am_path = $this->am_dist_path;
        $mm_path = $this->mm_path;
        $vendor_code = $this->vendor_code;
        foreach ($dirs as $dir) {
            $source = $mm_path . $dir;
            if (is_dir($source)) {
                $this->delfiles($source, $vendor_code);
            }
        }
        $this->delviewdirs($mm_path . 'View', $vendor_code);
        return true;
    }

    /**
     * 删除目录中的所有文件
     * @param type $source
     * @param type $vendor_code
     * @since 1.0 <2015-8-29> SoChishun Added.
     */
    protected function delfiles($source, $vendor_code) {
        if ($handle = opendir($source)) {
            while (false !== ( $f = readdir($handle) )) {
                if ('.' == $f || '..' == $f || 0 !== strpos($f, $vendor_code)) {
                    continue;
                }
                $sourcetmp = $source . '/' . $f;
                if (is_file($sourcetmp)) {
                    unlink($sourcetmp);
                }
            }
            closedir($handle);
        }
    }

    /**
     * 删除目录
     * @param string $source
     * @since 1.0 <2015-8-29> SoChishun Added.
     */
    protected function rmdir($source) {
        if (is_dir($source)) {
            if ($handle = opendir($source)) {
                while (false !== ( $f = readdir($handle) )) {
                    if ('.' == $f || '..' == $f) {
                        continue;
                    }
                    $sourcetmp = $source . '/' . $f;
                    if (is_dir($sourcetmp)) {
                        $this->rmdir($sourcetmp);
                    } else {
                        unlink($sourcetmp);
                    }
                }
                closedir($handle);
            }
            rmdir($source);
        }
    }

    /**
     * 删除视图中的目录
     * @param type $source
     * @param type $vendor_code
     * @since 1.0 <2015-8-29> SoChishun Added.
     */
    protected function delviewdirs($source, $vendor_code) {
        if (is_dir($source)) {
            if ($handle = opendir($source)) {
                while (false !== ( $f = readdir($handle) )) {
                    if ('.' == $f || '..' == $f) {
                        continue;
                    }
                    $sourcetmp = $source . '/' . $f;
                    if (is_file($sourcetmp)) {
                        continue;
                    }
                    if (0 === strpos($f, $vendor_code)) {
                        $this->rmdir($sourcetmp);
                    } else {
                        $this->delviewdirs($sourcetmp, $vendor_code);
                    }
                }
                closedir($handle);
            }
        }
    }

    /**
     * 打开数据库连接
     * @param string $mm 主模块名称
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     */
    protected function db_connect($mm, &$msg = '') {
        $cnf_path = $this->mm_path . 'Conf/config.php';
        if (!file_exists($cnf_path)) {
            $msg = '主模块[' . $mm . ']配置文件不存在!';
            return false;
        }
        $config = @require $cnf_path;
        // 打开数据库连接
        $link = @mysql_connect($config['DB_HOST'], $config['DB_USER'], $config['DB_PWD']);
        if (!$link) {
            $msg = mysql_error();
            return false;
        }
        $res = @mysql_select_db($config['DB_NAME'], $link);
        if (false === $res) {
            $msg = mysql_error();
            mysql_close($link);
            return false;
        }
        if (isset($config['DB_CHARSET'])) {
            $res = @mysql_query('set names ' . $config['DB_CHARSET'], $link);
            if (false === $res) {
                $msg = mysql_error();
                mysql_close($link);
                return false;
            }
        }
        return $link;
    }

}
?>
